#include "JsonOperate.h"

#include "Common/StringUtil.h"
#include "Log/Log.h"
#include "Poco/File.h"
#include "Poco/FileStream.h"
#include "Poco/JSON/Parser.h"
#include "Poco/StringTokenizer.h"
#include "JsonPath/JsonPath.h"

JsonOperate::JsonOperate()
{
    m_bInit = false;
    m_pJsonArrTemp = new Poco::JSON::Array();
    m_pJsonObjTemp = new Poco::JSON::Object();
    m_eResultType = JsonOperateResultType_Max;
}

Poco::Dynamic::Var JsonOperate::Get(std::string strJsonPath)
{
    return Operate(JsonOperateType_Get, strJsonPath);
}

int JsonOperate::Set(std::string strJsonPath, Poco::Dynamic::Var var)
{
    Operate(JsonOperateType_Set, strJsonPath, var);
    return 0;
}

int JsonOperate::Add(std::string strJsonPath, Poco::Dynamic::Var var)
{
    Operate(JsonOperateType_Add, strJsonPath, var);
    return 0;
}

int JsonOperate::Del(std::string strJsonPath)
{
    Operate(JsonOperateType_Del, strJsonPath);
    return 0;
}

Poco::Dynamic::Var JsonOperate::Operate(JsonOperateType eOpt, std::string strJsonPath, Poco::Dynamic::Var var)
{
    Poco::Mutex::ScopedLock lock(m_mutex);
    m_eOperateType = eOpt;
    JsonPath jsonPath(strJsonPath);
    if (jsonPath.IsValid() == false)
    {
        zlog.SError("json path [%s] is invalid\n", strJsonPath.c_str());
        return Poco::Dynamic::Var();
    }
    Poco::JSON::Object::Ptr pObj = m_pObj;
    Poco::JSON::Array::Ptr pArr = NULL;
    if (eOpt == JsonOperateType_Set)
    {
        m_varTemp = var;
    }
    if (Trace(jsonPath, pObj, pArr) != 0)
    {
        zlog.SError("trace json path [%s] error\n", strJsonPath.c_str());
    }
    if (eOpt == JsonOperateType_Get)
    {
        switch (m_eResultType)
        {
        case JsonOperateResultType_JsonObj: var = Poco::JSON::Object::makeStruct(m_pJsonObjTemp); break;
        case JsonOperateResultType_JsonArr: var = Poco::JSON::Array::makeArray(m_pJsonArrTemp); break;
        case JsonOperateResultType_Var: var = m_varTemp; break;
        default: zlog.SError("%s: json path [%s] result type [%d] error\n", __PRETTY_FUNCTION__, strJsonPath.c_str(), m_eResultType);break;
        }
    }
    Reset();
    return var;
}

int JsonOperate::SetObject(Poco::JSON::Object::Ptr& pObjOrigin, Poco::JSON::Object::Ptr pObjNew)
{
    if (pObjNew.isNull() || pObjOrigin.isNull())
    {
        return -1;
    }
    for (Poco::JSON::Object::Iterator i = pObjNew->begin(); i != pObjNew->end(); ++i)
    {
        if (pObjOrigin->has(i->first) == false)
        {
            continue;
        }
        Poco::Dynamic::Var var = pObjOrigin->get(i->first);
        if (i->second.type() != var.type())
        {
            continue;
        }
        if (i->second.isArray())
        {
            //SetArray();
            continue;
        }
        else if (var.isArray() || var.isInteger() || var.isSigned() || var.isNumeric() || var.isBoolean() || var.isString())
        {
            pObjOrigin->set(i->first, pObjNew->get(i->first));
        }
        else
        {
            Poco::JSON::Object::Ptr pObj = pObjOrigin->getObject(i->first);
            SetObject(pObj, pObjNew->getObject(i->first));
        }
    }
    return 0;
}

int JsonOperate::GetNext(Poco::JSON::Object::Ptr& pObj, Poco::JSON::Array::Ptr& pArr, std::string strKey)
{
    try
    {
        if (pObj.isNull() == false)
        {
            //Poco::Dynamic::Var var = pObj->get(*i);
            //zlog.SInfo("key %s arryr %d vector %d list %d deque %d struct %d ordered %d int %d signed %d numberic %d bool %d string %d date %d\n", i->c_str(), var.isArray(), var.isVector(), var.isList(), var.isDeque(), var.isStruct(), var.isOrdered(), var.isInteger(), var.isSigned(), var.isNumeric(), var.isBoolean(), var.isString(), var.isDate());
            if (pObj->has(strKey) == false)
            {
                zlog.SError("not find key 2 [%s]\n", strKey.c_str());
                return -1;
            }
            if (pObj->get(strKey).isArray())
            {
                pArr = pObj->getArray(strKey);
                pObj = NULL;
            }
            else
            {
                pObj = pObj->getObject(strKey);
                pArr = NULL;
            }
            return 0;
        }
        else if (pArr.isNull() == false)
        {
            int iIndex = StringUtil::String2Int(strKey);
            if (pArr->size() > iIndex && iIndex >= 0)
            {
                pObj = pArr->getObject(iIndex);
                pArr = NULL;
                return 0;
            }
            else
            {
                zlog.SError("index error [%d]\n", iIndex);
                return -1;
            }
        }
        else
        {
            zlog.SError("not find type for key [%s]\n");
            return -1;
        }
    }
    catch (...)
    {
        zlog.SError("%s catched key %s\n", __PRETTY_FUNCTION__, strKey.c_str());
        return -1;
    }
}

int JsonOperate::Trace(JsonPath jsonPath, Poco::JSON::Object::Ptr& pObj, Poco::JSON::Array::Ptr& pArr)
{
    for (size_t i = 0; i < jsonPath.m_vectElement.size(); ++i)
    {
        JsonPathToken& jpEle = jsonPath.m_vectElement[i];
        if (jpEle.m_eType == JsonPathTokenType_Key)
        {
            if (i + 1 < jsonPath.m_vectElement.size())
            {
                int iNext = GetNext(pObj, pArr, jpEle.m_strTokenName);
                if (iNext != 0)
                {
                    zlog.SError("get next error\n");
                    return -1;
                }
            }
            else
            {
                if(jpEle.m_strTokenName == "*")
                {
                    m_eResultType = JsonOperateResultType_JsonObj;
                    m_pJsonObjTemp = pObj;
                }
                else if (pObj->has(jpEle.m_strTokenName) == false)
                {
                    zlog.SError("not find key [%s]\n", jpEle.m_strTokenName.c_str());
                    return -1;
                }
                else if (pObj->get(jpEle.m_strTokenName).isArray())
                {
                    m_eResultType = JsonOperateResultType_JsonArr;
                    m_pJsonArrTemp = pObj->getArray(jpEle.m_strTokenName);
                    //zlog.SWarn("这里感觉有问题，应该add，而不是直接赋值\n");
                }
                else if (pObj->get(jpEle.m_strTokenName).isInteger() || pObj->get(jpEle.m_strTokenName).isSigned() || pObj->get(jpEle.m_strTokenName).isNumeric() || pObj->get(jpEle.m_strTokenName).isBoolean() || pObj->get(jpEle.m_strTokenName).isString())
                {
                    if (m_eResultType == JsonOperateResultType_Max)
                    {
                        if (m_eOperateType == JsonOperateType_Get)
                        {
                            m_eResultType = JsonOperateResultType_Var;
                            m_varTemp = pObj->get(jpEle.m_strTokenName);
                        }
                        else if (m_eOperateType == JsonOperateType_Set)
                        {
                            if (pObj->get(jpEle.m_strTokenName).type() == m_varTemp.type())
                            {
                                pObj->set(jpEle.m_strTokenName, m_varTemp);
                            }
                            else if (pObj->get(jpEle.m_strTokenName).type() == typeid(long int) && m_varTemp.type() == typeid(int))
                            {
                                pObj->set(jpEle.m_strTokenName, m_varTemp.convert<long int>());
                            }
                            else
                            {
                                zlog.SError("jsonpath 0 result type error index %d result %d [%s], typeName is [%s]:[%s]\n", i, m_eResultType, jpEle.m_strTokenName.c_str(), m_varTemp.type().name(), pObj->get(jpEle.m_strTokenName).type().name());
                            }
                        }
                    }
                    else if (m_eResultType == JsonOperateResultType_JsonArr)
                    {
                        if (m_eOperateType == JsonOperateType_Get)
                        {
                            m_pJsonArrTemp->add(pObj->get(jpEle.m_strTokenName));
                        }
                        else if (m_eOperateType == JsonOperateType_Set)
                        {
                            Poco::JSON::Object::Ptr pObjTemp = pObj->getObject(jpEle.m_strTokenName);
                            SetObject(pObjTemp, m_pJsonObjTemp);
                        }
                    }
                    else
                    {
                        zlog.SError("jsonpath 1 result type error index %d result %d [%s]\n", i, m_eResultType, jpEle.m_strTokenName.c_str());
                    }
                }
                else
                {
                    if (m_eOperateType == JsonOperateType_Get)
                    {
                        m_eResultType = JsonOperateResultType_JsonObj;
                        m_pJsonObjTemp = pObj->getObject(jpEle.m_strTokenName);
                    }
                    else if (m_eOperateType == JsonOperateType_Set)
                    {
                        Poco::JSON::Object::Ptr pObjTemp = pObj->getObject(jpEle.m_strTokenName);
                        SetObject(pObjTemp, m_pJsonObjTemp);
                    }
                }
            }
        }
        else if (jpEle.m_eType == JsonPathTokenType_SquareBracketL)
        {
            JsonPath subJsonPath = jsonPath;
            subJsonPath.m_vectElement.erase(subJsonPath.m_vectElement.begin(), subJsonPath.m_vectElement.begin() + i);
            if (Walk(pObj, pArr, subJsonPath) != 0)
            {
                return -1;
            }
            else
            {
                return 0;
            }
        }
        else if (jpEle.m_eType == JsonPathTokenType_DotDot)
        {
            JsonPath subJsonPath = jsonPath;
            subJsonPath.m_vectElement.erase(subJsonPath.m_vectElement.begin(), subJsonPath.m_vectElement.begin() + i + 1);
            if (Recursion(pObj, pArr, subJsonPath) != 0)
            {
                return -1;
            }
            else
            {
                return 0;
            }
        }
        else
        {
            zlog.SError("error for json path\n");
            return -1;
        }
    }
    return 0;
}

int JsonOperate::Walk(Poco::JSON::Object::Ptr& pObj, Poco::JSON::Array::Ptr& pArr, JsonPath subJsonPath)
{
    if (pArr.isNull() || !subJsonPath.IsValid())
    {
        zlog.SError("pArr is NULL [%d] subJsonPath is valid [%d]\n", pArr.isNull(), subJsonPath.IsValid());
        return -1;
    }

    m_eResultType = JsonOperateResultType_JsonArr;
    JsonPathToken& jpEle = subJsonPath.m_vectElement.front();
    if (jpEle.m_eType == JsonPathTokenType_SquareBracketL)
    {
        JsonPathSquareBracket* p = (JsonPathSquareBracket*)jpEle.m_pUserData;
        JsonPath subJsonPath2 = subJsonPath;
        subJsonPath2.m_vectElement.erase(subJsonPath2.m_vectElement.begin());
        if (p->m_eType == JsonPathSquareBracket::JsonPathSquareBracketType_Index)
        {
            for (int i = 0; i < p->m_jpiIndex.m_vectDiscreteIndex.size(); ++i)
            {
                int iIndex = p->m_jpiIndex.m_vectDiscreteIndex[i];
                if (pArr->size() > iIndex && iIndex >= 0)
                {
                    if (subJsonPath2.m_vectElement.size() == 0)
                    {
                        if (m_eOperateType == JsonOperateType_Get)
                        {
                            m_pJsonArrTemp->add(pArr->get(iIndex));
                        }
                        else if (m_eOperateType == JsonOperateType_Set)
                        {
                            Poco::JSON::Object::Ptr pObjTemp = pArr->getObject(iIndex);
                            SetObject(pObjTemp, m_pJsonObjTemp);
                        }
                    }
                    else
                    {
                        Poco::JSON::Object::Ptr pSubObj = pArr->getObject(iIndex);
                        if (pSubObj.isNull())
                        {
                            continue;
                        }
                        Poco::JSON::Array::Ptr pSubArr = NULL;
                        if (Trace(subJsonPath2, pSubObj, pSubArr) != 0)
                        {
                            return -1;
                        }
                    }
                }
            }
            for (int i = 0; i < p->m_jpiIndex.m_vectRangeIndex.size(); ++i)
            {
                int iStart = p->m_jpiIndex.m_vectRangeIndex[i].m_iStart;
                int iEnd = p->m_jpiIndex.m_vectRangeIndex[i].m_bEnd ? p->m_jpiIndex.m_vectRangeIndex[i].m_iEnd : pArr->size();
                int iStep = p->m_jpiIndex.m_vectRangeIndex[i].m_iStep;
                if (iStart < 0)iStart = pArr->size() + iStart;
                if (iEnd < 0)iEnd = pArr->size() + iEnd;
                if (iStart >= iEnd || iStart < 0 || iEnd < 0)continue;

                for (int i = iStart; i < iEnd; i += iStep)
                {
                    if (subJsonPath2.m_vectElement.size() == 0)
                    {
                        if (m_eOperateType == JsonOperateType_Get)
                        {
                            m_pJsonArrTemp->add(pArr->get(i));
                        }
                        else if (m_eOperateType == JsonOperateType_Set)
                        {
                            Poco::JSON::Object::Ptr pObjTemp = pArr->getObject(i);
                            SetObject(pObjTemp, m_pJsonObjTemp);
                        }
                    }
                    else
                    {
                        Poco::JSON::Object::Ptr pSubObj = pArr->getObject(i);
                        if (pSubObj.isNull())
                        {
                            continue;
                        }
                        Poco::JSON::Array::Ptr pSubArr = NULL;
                        if (Trace(subJsonPath2, pSubObj, pSubArr) != 0)
                        {
                            return -1;
                        }
                    }
                }
            }
        }
        else if (p->m_eType == JsonPathSquareBracket::JsonPathSquareBracketType_FilterExpr)
        {
            for (int i = 0; i < pArr->size(); ++i)
            {
                Poco::JSON::Object::Ptr pSubObj = pArr->getObject(i);
                if (pSubObj.isNull())
                {
                    continue;
                }
                if (p->m_vecFilterExpr[0].ExprResult(pSubObj, true) == true)
                {
                    Poco::JSON::Array::Ptr pSubArr = NULL;
                    if (subJsonPath2.m_vectElement.size() == 0)
                    {
                        if (m_eOperateType == JsonOperateType_Get)
                        {
                            m_pJsonArrTemp->add(pSubObj);
                        }
                        else if (m_eOperateType == JsonOperateType_Set)
                        {
                            SetObject(pSubObj, m_pJsonObjTemp);
                        }
                    }
                    else if (Trace(subJsonPath2, pSubObj, pSubArr) != 0)
                    {
                        zlog.SError("walk: trace error\n");
                        return -1;
                    }
                }
                else
                {
                    zlog.SWarn("filter expr [%s] not match\n", p->m_vecFilterExpr[0].m_stData.m_strKey.c_str());
                }
            }
        }
        else if (p->m_eType == JsonPathSquareBracket::JsonPathSquareBracketType_Wildcard)
        {
            if (subJsonPath2.m_vectElement.empty())
            {
                for(int i = 0; i < pArr->size(); ++i)
                {
                    if (m_eOperateType == JsonOperateType_Get)
                    {
                        m_pJsonArrTemp->add(pArr->get(i));
                    }
                    else if (m_eOperateType == JsonOperateType_Set)
                    {
                        Poco::JSON::Object::Ptr pObjTemp = pArr->getObject(i);
                        SetObject(pObjTemp, m_pJsonObjTemp);
                    }
                }
            }
            else
            {
                for (int i = 0; i < pArr->size(); ++i)
                {
                    Poco::JSON::Object::Ptr pSubObj = pArr->getObject(i);
                    if (pSubObj.isNull())
                    {
                        continue;
                    }
                    Poco::JSON::Array::Ptr pSubArr = NULL;
                    if (Trace(subJsonPath2, pSubObj, pSubArr) != 0)
                    {
                        return -1;
                    }
                }
            }
        }
        else
        {
            zlog.SError("walk: square bracket type error\n");
            return -1;
        }
    }
    else
    {
        zlog.SError("Walk: error for json path\n");
        return -1;
    }
    return 0;
}

int JsonOperate::Recursion(Poco::JSON::Object::Ptr& pObj, Poco::JSON::Array::Ptr& pArr, JsonPath subJsonPath)
{
    if ((pArr.isNull() && pObj.isNull()) || !subJsonPath.IsValid())
    {
        zlog.SError("pArr is NULL [%d] pObj is NULL [%d] subJsonPath is valid [%d]\n", pArr.isNull(), pObj.isNull(), subJsonPath.IsValid());
        return -1;
    }

    m_eResultType = JsonOperateResultType_JsonArr;
    if (!pArr.isNull())
    {
        for (int i = 0; i < pArr->size(); ++i)
        {
            Poco::JSON::Object::Ptr pSubObj = pArr->getObject(i);
            if (pSubObj.isNull())continue;
            Poco::JSON::Array::Ptr pSubArr = NULL;
            if (Recursion(pSubObj, pSubArr, subJsonPath) != 0)
            {
                return -1;
            }
        }
    }
    else //if (!pObj.isNull())
    {
        JsonPathToken& jpEle = subJsonPath.m_vectElement.front();
        if (jpEle.m_eType == JsonPathTokenType_Key)
        {
            JsonPath subJsonPath2 = subJsonPath;
            subJsonPath2.m_vectElement.erase(subJsonPath2.m_vectElement.begin());
            if (pObj->has(jpEle.m_strTokenName) == true && subJsonPath.m_vectElement.size() == 1)
            {
                m_pJsonArrTemp->add(pObj->get(jpEle.m_strTokenName));
            }
            else if (pObj->has(jpEle.m_strTokenName) == true /*&& subJsonPath.m_vectElement.size() > 1*/)
            {
                Poco::JSON::Object::Ptr pSubObj = pObj->getObject(jpEle.m_strTokenName);
                Poco::JSON::Array::Ptr pSubArr = pObj->getArray(jpEle.m_strTokenName);
                if (pSubObj.isNull() == false)
                {
                    Poco::JSON::Array::Ptr pSubArr = NULL;
                    if (Trace(subJsonPath2, pSubObj, pSubArr) != 0)
                    {
                    }
                }
                else if(pSubArr.isNull() == false)
                {
                    Poco::JSON::Object::Ptr pSubObj = NULL;
                    if (Walk(pSubObj, pSubArr, subJsonPath2) != 0)
                    {
                    }
                }
            }
            else if(jpEle.m_strTokenName == "*" && subJsonPath.m_vectElement.size() == 1)
            {
                if (m_eOperateType == JsonOperateType_Get)
                {
                    m_pJsonArrTemp->add(pObj);
                }
                else if (m_eOperateType == JsonOperateType_Set)
                {
                    SetObject(pObj, m_pJsonObjTemp);
                }
                return 0;
            }
            else if (jpEle.m_strTokenName == "*" && subJsonPath.m_vectElement.size() > 1)
            {
                Recursion(pObj, pArr, subJsonPath2);
            }

            for (Poco::JSON::Object::Iterator i = pObj->begin(); i != pObj->end(); ++i)
            {
                if (pObj->getArray(i->first).isNull() == false)
                {
                    Poco::JSON::Object::Ptr pSubObj = NULL;
                    Poco::JSON::Array::Ptr pSubArr = pObj->getArray(i->first);
                    Recursion(pSubObj, pSubArr, subJsonPath);
                    continue;
                }
                else if (pObj->getObject(i->first).isNull())
                {
                    continue;
                }
                Poco::JSON::Object::Ptr pSubObj = pObj->getObject(i->first);
                Poco::JSON::Array::Ptr pSubArr = NULL;
                Recursion(pSubObj, pSubArr, subJsonPath);
            }
        }
        else if (jpEle.m_eType == JsonPathTokenType_SquareBracketL)
        {
            for (Poco::JSON::Object::Iterator i = pObj->begin(); i != pObj->end(); ++i)
            {
                if(pObj->getArray(i->first).isNull() == false)
                {
                    Poco::JSON::Object::Ptr pSubObj = NULL;
                    Poco::JSON::Array::Ptr pSubArr = pObj->getArray(i->first);
                    Walk(pSubObj, pSubArr, subJsonPath);
                    Recursion(pSubObj, pSubArr, subJsonPath);
                }
                else if(pObj->getObject(i->first).isNull() == false)
                {
                    Poco::JSON::Object::Ptr pSubObj = pObj->getObject(i->first);
                    Poco::JSON::Array::Ptr pSubArr = NULL;
                    Recursion(pSubObj, pSubArr, subJsonPath);
                }
                else if(pObj->has(i->first) == true)
                {
                    JsonPathSquareBracket* p = (JsonPathSquareBracket*)jpEle.m_pUserData;
                    if (p->m_eType == JsonPathSquareBracket::JsonPathSquareBracketType_Index)
                    {
                    }
                    else if (p->m_eType == JsonPathSquareBracket::JsonPathSquareBracketType_FilterExpr)
                    {
                        if (p->m_vecFilterExpr[0].ExprResult(pObj, true) == true)
                        {
                            if (m_eOperateType == JsonOperateType_Get)
                            {
                                m_pJsonArrTemp->add(pObj);
                            }
                            else if (m_eOperateType == JsonOperateType_Set)
                            {
                                SetObject(pObj, m_pJsonObjTemp);
                            }
                            break;
                        }
                    }
                }
            }
        }
        else if (jpEle.m_eType == JsonPathTokenType_DotDot)
        {
            zlog.SWarn("recursion: dot dot not support\n");
        }
    }
    return 0;
}

void JsonOperate::Reset()
{
    m_eResultType = JsonOperateResultType_Max;
    m_pJsonArrTemp->clear();
}
